package com.comall.dc.banktest.application;

import com.comall.dc.banktest.application.command.*;
import com.comall.dc.banktest.domain.AccountDomainService;
import com.comall.dc.banktest.domain.exception.*;
import com.comall.dc.banktest.domain.model.Account;
import com.comall.dc.banktest.domain.model.AccountRecord;
import com.comall.dc.banktest.domain.repository.AccountRecordRepository;
import com.comall.dc.banktest.domain.repository.AccountRepository;
import com.comall.dc.banktest.infrastructure.core.BalanceOperationEnum;
import com.comall.dc.banktest.query.AccountQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

@Service
public class AccountApplication {

    @Autowired
    private AccountDomainService accountDomainService;

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private AccountRecordRepository accountRecordRepository;


    /**
     * 开户操作
     * @param command 开户命令
     * @return 账户信息
     */
    public Account createAccount(CreateAccountCommand command) throws IdCardExistException{

        if (accountDomainService.isIdCardExist(command.getIdCard())){
            throw new IdCardExistException();
        }

        Account account = new Account(command.getIdCard(), command.getUserName(),
                command.getUserPhone(), command.getUserAddress(), command.getUserJob());
        account = accountRepository.save(account);
        return account;
    }

    /**
     * 激活账户操作
     * @param command 激活账户命令
     * @return 账户信息
     * @throws AccountNoAuthException 账户未授权异常
     * @throws AccountNotExistException 账户不存在异常
     */
    public Account activateAccount(ActivateAccountCommand command) throws AccountNoAuthException, AccountNotExistException{
        Account account = accountRepository.load(command.getId());
        if (!account.getIdCard().equals(command.getIdCard())){
            throw new AccountNoAuthException();
        }
        account.activate(command.getPassword());
        accountRepository.save(account);
        return account;
    }

    /**
     * 冻结账户操作
     * @param command 冻结账户命令
     * @return 账户信息
     * @throws AccountNotExistException 账户不存在异常
     */
    public Account freezeAccount(FreezeAccountCommand command) throws AccountNotExistException{
        Account account = accountRepository.load(command.getId());
        account.freeze();
        accountRepository.save(account);
        return account;
    }



    /**
     * 存款操作, 包含账目记录操作
     * @param command 存款命令
     * @return 最终账户余额
     * @throws AccountNoAuthException 账户未授权异常
     * @throws AccountFreezeException 账户冻结异常
     * @throws AccountNotExistException 账户不存在异常
     */
    @Transactional
    public BigDecimal depositBalance(DepositBalanceCommand command)
            throws AccountNoAuthException, AccountFreezeException, AccountNotExistException{
        Account account = accountRepository.load(command.getId());
        if (!account.isAuthPass(command.getPassword())){
            throw new AccountNoAuthException();
        }

        if (account.isFrozen()){
            throw new AccountFreezeException();
        }

        account.addBalance(command.getValue());
        account = accountRepository.save(account);

        AccountRecord accountRecord = new AccountRecord(BalanceOperationEnum.DEPOSIT, command.getId(), null, command.getValue());
        accountRecordRepository.save(accountRecord);
        return account.getBalance();
    }

    /**
     * 取款操作, 包含账目记录操作
     * @param command 取款命令
     * @return 最终账户余额
     * @throws AccountNoAuthException 账户未授权异常
     * @throws AccountFreezeException 账户冻结异常
     * @throws BalanceInsufficientException 账户余额不足异常
     * @throws AccountNotExistException 账户不存在异常
     */
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public BigDecimal drawBalance(DrawBalanceCommand command)
            throws AccountNotExistException, AccountNoAuthException, AccountFreezeException, BalanceInsufficientException {
        Account account = accountRepository.load(command.getId());
        if (!account.isAuthPass(command.getPassword())){
            throw new AccountNoAuthException();
        }

        if (account.isFrozen()){
            throw new AccountFreezeException();
        }

        account.subtractBalance(command.getValue());
        account = accountRepository.save(account);

        AccountRecord accountRecord = new AccountRecord(BalanceOperationEnum.DRAW, command.getId(), null, command.getValue());
        accountRecordRepository.save(accountRecord);

        return account.getBalance();
    }

    /**
     * 转账操作, 包含账目记录操作
     * @param command 转账命令
     * @return 最终本账户余额
     * @throws AccountNotExistException 账户不存在异常
     * @throws AccountFreezeException 账户冻结异常
     * @throws AccountNoAuthException 账户未授权异常
     * @throws BalanceInsufficientException 账户余额不足异常
     */
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public BigDecimal transferBalance(TransferBalanceCommand command)
            throws AccountNotExistException, AccountFreezeException, AccountNoAuthException, BalanceInsufficientException {
        Account account = accountDomainService.transferBalance(command.getId(), command.getTargetId(), command.getPassword(), command.getValue());
        return account.getBalance();

    }

    public void validPassword(Integer id, String password) throws AccountNoAuthException, AccountNotExistException{
        if (!accountRepository.load(id).isAuthPass(password)){
            throw new AccountNoAuthException();
        }
    }

}
